#include "http_parser.h"
#include "qtch/log.h"
#include "qtch/config.h"

namespace qtch{
namespace http{

static Logger::ptr logger = QTCH_LOG_NAME("system");

static ConfigVar<uint64_t>::ptr g_http_request_buffer_size = 
        Config::LookUp("http.request.buffer_size", ((uint64_t)4 * 1024), "http request buffer size");

static ConfigVar<uint64_t>::ptr g_http_request_max_body_size = 
        Config::LookUp("http.request.max_body_size", ((uint64_t)64 *1024 * 1024), "http request max body size");

static ConfigVar<uint64_t>::ptr g_http_response_buffer_size = 
        Config::LookUp("http.response.buffer_size", ((uint64_t)4 * 1024), "http response buffer size");

static ConfigVar<uint64_t>::ptr g_http_response_max_body_size = 
        Config::LookUp("http.response.max_body_size", ((uint64_t)64 *1024 * 1024), "http response max body size");

static uint64_t s_http_request_buffer_size = 0;
static uint64_t s_http_request_max_body_size = 0;
static uint64_t s_http_response_buffer_size = 0;
static uint64_t s_http_response_max_body_size = 0;


struct _RequestSizeInit  {
    _RequestSizeInit() {
        s_http_request_buffer_size = g_http_request_buffer_size->getValue();
        s_http_request_max_body_size = g_http_request_max_body_size->getValue();
        s_http_response_buffer_size = g_http_response_buffer_size->getValue();
        s_http_response_max_body_size = g_http_response_max_body_size->getValue();

        g_http_request_buffer_size->addListener([](const uint64_t& ov, const uint64_t& nv){
            s_http_request_buffer_size = nv;
        });

        g_http_request_max_body_size->addListener([](const uint64_t& ov, const uint64_t& nv){
            s_http_request_max_body_size = nv;
        });

        g_http_response_buffer_size->addListener([](const uint64_t& ov, const uint64_t& nv){
            s_http_response_buffer_size = nv;
        });

        g_http_response_max_body_size->addListener([](const uint64_t& ov, const uint64_t& nv){
            s_http_response_max_body_size = nv;
        });
    }
};

static _RequestSizeInit _init;

void on_request_header_field_done(void *data, const char *field, size_t flen, const char *value, size_t vlen){
    HttpRequestParser* parser = static_cast<HttpRequestParser*>(data);
    if(flen == 0){
        QTCH_LOG_WARN(logger) << "invalid http request field length == 0";
        return;
    }
    parser->getData()->setHeader(std::string(field,flen)
                                ,std::string(value,vlen));
}

void on_request_method_done(void *data, const char *at, size_t length){
    HttpRequestParser* parser = static_cast<HttpRequestParser*>(data);
    std::string value = std::string(at,length);
    HttpMethod m = StringToHttpMethod(value);
    if(m == HttpMethod::INVALID_METHOD){
        QTCH_LOG_WARN(logger) << "invalid http request method: " << value;
        parser->setError(1000);
        return;
    }
    parser->getData()->setMethod(m);
}

void on_request_uri_done(void *data, const char *at, size_t length){

}

void on_request_fragment_done(void *data, const char *at, size_t length){
    HttpRequestParser* parser = static_cast<HttpRequestParser*>(data);
    parser->getData()->setFragment(std::string(at,length));
}

void on_request_path_done(void *data, const char *at, size_t length){
    HttpRequestParser* parser = static_cast<HttpRequestParser*>(data);
    parser->getData()->setPath(std::string(at,length));
}

void on_request_query_string_done(void *data, const char *at, size_t length){
    HttpRequestParser* parser = static_cast<HttpRequestParser*>(data);
    parser->getData()->setQuery(std::string(at,length));
}

void on_request_http_version_done(void *data, const char *at, size_t length){
    HttpRequestParser* parser = static_cast<HttpRequestParser*>(data);
    uint8_t v = 0;
    if(strncmp(at, "HTTP/1.1",length) == 0){
        v = 0x11;
    } else if(strncmp(at, "HTTP/1.0",length) == 0){
        v = 0x10;
    } else{
        QTCH_LOG_WARN(logger) << "invalid http request version: " << std::string(at,length);
        parser->setError(1001);
        return;
    }
    parser->getData()->setVersion(v);
}

void on_request_header_done(void *data, const char *at, size_t length){
}


HttpRequestParser::HttpRequestParser()
    :m_error(0){
    m_data.reset(new HttpRequest);
    http_parser_init(&m_parser);
    m_parser.http_field = on_request_header_field_done;
    m_parser.request_method = on_request_method_done;
    m_parser.request_uri = on_request_uri_done;
    m_parser.fragment = on_request_fragment_done;
    m_parser.request_path = on_request_path_done;
    m_parser.query_string = on_request_query_string_done;
    m_parser.http_version = on_request_http_version_done;
    m_parser.header_done = on_request_header_done;
    m_parser.data = this;
}

size_t HttpRequestParser::execute(char* data, size_t len){
    size_t offset = http_parser_execute(&m_parser, data, len, 0);
    memmove(data, data + offset,(len - offset));
    return offset;
}

int HttpRequestParser::isFinished(){
    return http_parser_finish(&m_parser);
}

int HttpRequestParser::hasError(){
    return m_error || http_parser_has_error(&m_parser);
}

uint64_t HttpRequestParser::getContentLength(){
    return m_data->getHeaderAs<uint64_t>("content-length",0);
}

uint64_t HttpRequestParser::GetHttpRequestBufferSize(){
    return s_http_request_buffer_size;
}

uint64_t HttpRequestParser::GetHttpRequestMaxBodySize(){
    return s_http_request_max_body_size;
}




void on_response_header_field_done(void *data, const char *field, size_t flen, const char *value, size_t vlen){
    HttpResponseParser* parser = static_cast<HttpResponseParser*>(data);
    if(flen == 0){
        QTCH_LOG_WARN(logger) << "invalid http response field length == 0";
        return;
    }
    parser->getData()->setHeader(std::string(field,flen)
                                ,std::string(value,vlen));
}

void on_response_reason_done(void *data, const char *at, size_t length){
    HttpResponseParser* parser = static_cast<HttpResponseParser*>(data);
    parser->getData()->setReason(std::string(at,length));
}

void on_response_status_done(void *data, const char *at, size_t length){
    HttpResponseParser* parser = static_cast<HttpResponseParser*>(data);
    HttpStatus s = (HttpStatus)atoi(std::string(at,length).c_str());
    parser->getData()->setStatus(s);
}

void on_response_chunk_done(void *data, const char *at, size_t length){

}

void on_response_version_done(void *data, const char *at, size_t length){
    HttpResponseParser* parser = static_cast<HttpResponseParser*>(data);
    uint8_t v = 0;
    if(strncmp(at, "HTTP/1.1",length) == 0){
        v = 0x11;
    } else if(strncmp(at, "HTTP/1.0",length) == 0){
        v = 0x10;
    } else{
        QTCH_LOG_WARN(logger) << "invalid http response version: " << std::string(at,length);
        parser->setError(1001);
        return;
    }
    parser->getData()->setVersion(v);
}

void on_response_header_done(void *data, const char *at, size_t length){

}

void on_response_last_chunk_done(void *data, const char *at, size_t length){

}

HttpResponseParser::HttpResponseParser()
    :m_error(0){
    m_data.reset(new HttpResponse);
    httpclient_parser_init(&m_parser);

    m_parser.http_field = on_response_header_field_done;
    m_parser.reason_phrase = on_response_reason_done;
    m_parser.status_code = on_response_status_done;
    m_parser.chunk_size = on_response_chunk_done;
    m_parser.http_version = on_response_version_done;
    m_parser.header_done = on_response_header_done;
    m_parser.last_chunk = on_response_last_chunk_done;
    m_parser.data = this;
}

size_t HttpResponseParser::execute(char* data, size_t len,bool chunk){
    if(chunk){
        httpclient_parser_init(&m_parser);
    }

    size_t offset = httpclient_parser_execute(&m_parser, data, len, 0);
    if(isFinished()){
        offset--;
    }
    memmove(data, data + offset,(len - offset));
    return offset;
}

int HttpResponseParser::isFinished(){
    return httpclient_parser_finish(&m_parser);

}

int HttpResponseParser::hasError(){
    return m_error || httpclient_parser_has_error(&m_parser);
}

uint64_t HttpResponseParser::getContentLength(){
    return m_data->getHeaderAs<uint64_t>("content-length",0);
}

uint64_t HttpResponseParser::GetHttpResponseBufferSize(){
    return s_http_response_buffer_size;
}

uint64_t HttpResponseParser::GetHttpResponseMaxBodySize(){
    return s_http_response_max_body_size;
}

}
}